//=============================================================================

#include <windows.h>
#include <gl/gl.h>
#include <gl/glu.h>
#include <fstream>
#include "../../../../inc/aTexture.h"

//=============================================================================

using namespace std;

//=============================================================================

aTexture::aTexture()
{
  data = 0;
  texture = 0;
  channels = 0;
  width = 0;
  height = 0;
}

//=============================================================================

bool aTexture::atLoadTextureTGA(const aString& path)
{
  if (path.asGetLength() <= 0)
    return 0;

  atRemoveData();

  unsigned char length = 0;
  unsigned char bits = 0;
  unsigned char type = 0;
  int stride = 0;
  int i = 0;
  fstream file;
  unsigned long move = 0;

  file.open(path.asGetString(), ios_base::in | ios_base::binary);
  
  if (!file.is_open())
    return 0;

  file.read((char*)&length, sizeof(length));
  file.seekg(1, ios_base::cur);
  file.read((char*)&type, sizeof(type));
  file.seekg(9, ios_base::cur);
  file.read((char*)&width, sizeof(width));
  file.read((char*)&height, sizeof(height));
  file.read((char*)&bits, sizeof(bits));
  file.seekg(length + 1, ios_base::cur);

  //if (width != height || ((width%2)!= 0))
  //  return false;

	if(type != 10) // RLE?
	{
    if(bits == 24 || bits == 32)
		{
			channels = bits >> 3;
			stride = channels*width;
			data = new unsigned char[stride * height];
      int temp = 0;
      unsigned char *pLine = 0;

			for(int y = 0; y < height; y++)
			{
				pLine = &(data[stride*y]);
				
        file.read((char*)pLine, sizeof(unsigned char)*stride);
				for(i = 0; i < stride; i += channels)
				{
					temp = pLine[i];

					pLine[i] = pLine[i + 2];
					pLine[i + 2] = temp;
				}
			}
		}
		else
      if(bits == 16)
		  {
			  unsigned short pixels = 0;
			  int r = 0, g = 0, b = 0;

        channels = 3;
			  stride = channels*width;
			  data = new unsigned char[stride*height];

  			for(i = 0; i < width*height; i++)
	  		{
          file.read((char*)&pixels, sizeof(unsigned short));
			  	b = (pixels & 0x1f) << 3;
				  g = ((pixels >> 5) & 0x1f) << 3;
				  r = ((pixels >> 10) & 0x1f) << 3;
				
		  		data[i*3] = r;
			  	data[i*3 + 1] = g;
				  data[i*3 + 2] = b;
			  }
		}	
    else
    {
      file.close();

			return 0;
    }
	}
	else
	{
		unsigned char rleID = 0;
		int colorsRead = 0;
		channels = bits/8;
		stride = channels*width;

		data = new unsigned char[stride*height];
		unsigned char *pColors = new byte[channels];

		while(i < width*height)
		{
      file.read((char*)&rleID, sizeof(unsigned char));
			if(rleID < 128)
			{
				rleID++;
				while(rleID)
				{
          file.read((char*)pColors, sizeof(unsigned char)*channels);
					data[colorsRead] = pColors[2];
					data[colorsRead + 1] = pColors[1];
					data[colorsRead + 2] = pColors[0];
					if(bits == 32)
						data[colorsRead + 3] = pColors[3];
					i++;
					rleID--;
					colorsRead += channels;
				}
			}
			else
			{
				rleID -= 127;
        file.read((char*)pColors, sizeof(unsigned char)*channels);
				while(rleID)
				{
					data[colorsRead] = pColors[2];
					data[colorsRead + 1] = pColors[1];
					data[colorsRead + 2] = pColors[0];
					if(bits == 32)
						data[colorsRead + 3] = pColors[3];
					i++;
					rleID--;
					colorsRead += channels;
				}
			}
		}
		delete [] pColors;
	}

  file.close();
  atGenerateTextureForOGL();

  return 1;
}

//=============================================================================

void aTexture::atRemoveData()
{
  if (data)
  {
    delete [] data;
    
    data = 0;
  }

  if (texture)
  {
    glDeleteTextures(1, &texture);

    texture = 0;
  }
}

//=============================================================================

bool aTexture::atGenerateTextureForOGL()
{
  glGenTextures(1, &texture);

  if (!texture)
    return 0;

  glBindTexture(GL_TEXTURE_2D, texture);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexImage2D(GL_TEXTURE_2D, 0, (channels == 4 ? GL_RGBA : GL_RGB), width, height, 0, (channels == 4 ? GL_RGBA : GL_RGB), GL_UNSIGNED_BYTE, data);

  return 1;
}

//=============================================================================

bool aTexture::atIsNULL() const
{
  if (data == 0)
    return 1;

  return 0;
}

//=============================================================================

void aTexture::atBindTexture() const
{
  glBindTexture(GL_TEXTURE_2D, texture);
}

//=============================================================================

bool aTexture::atCollectDataTo(char* add) const
{
  if (!add)
    return false;

  memcpy(add, data, width*height*channels);

  return true;
}

//=============================================================================

bool aTexture::atAbsorbData(char* incomingData, unsigned short sizeLength, unsigned short bytes)
{
  if (!incomingData || (sizeLength <= 0) || (bytes <= 0))
    return 0;

  atRemoveData();

  width = sizeLength;
  height = sizeLength;
  channels = bytes;

  data = new unsigned char[width*height*channels];

  memcpy(data, incomingData, width*height*bytes);
  
  return 1;
}

//=============================================================================

void aTexture::atCloneTexture(const aTexture& tex)
{
  atRemoveData();

  channels = tex.channels;
  width = tex.width;
  height = tex.height;
  if (tex.data)
  {
    data = new unsigned char[width*height*channels];
    memcpy(data, tex.data, width*height*channels);

    atGenerateTextureForOGL();
  }
}

//=============================================================================

aTexture::~aTexture()
{
  atRemoveData();
}

//=============================================================================
